/*
 * @Author: yanjikun
 * @Date: 2022-01-07 15:00:57
 * @LastEditors: yanjikun
 * @LastEditTime: 2022-05-16 15:23:06
 * @Description: file content
 */
import { deepMerge } from '@/utils'
import { isArray, isFunction, isObject } from '@/utils/is'
import { NamePath } from 'ant-design-vue/lib/form/interface'
import { uniqBy } from 'lodash'
import { toRaw, unref } from 'vue'
import { FormConfig, FormEmitType } from '../types/form'
import { FormState } from './useFormState'
type UseFormActionContext = FormState & {
  emit: FormEmitType
  handleFormValues: (values: Recordable) => Recordable
}
export type FormEvents = ReturnType<typeof useFormEvents>
export const useFormEvents = (formActionContext: UseFormActionContext) => {
  const {
    emit,
    getFormProps,
    basicFormRef,
    formConfigRef,
    handleFormValues,
    formModel,
    defaultFormValues,
  } = formActionContext

  const setFieldsValue = async (values: Recordable): Promise<void> => {
    const configs = unref(formConfigRef)
    const fields = configs.map((item) => item.field).filter(Boolean)
    const validKeys: string[] = []
    Object.keys(values).forEach((key) => {
      let value = values[key]
      const hasKey = Reflect.has(values, key)
      if (hasKey && fields.includes(key)) {
        formModel[key] = value
        validKeys.push(key)
      }
    })
    validateFields(validKeys)
  }
  const getFieldsValue = (): Recordable => {
    const formEl = unref(basicFormRef)
    if (!formEl) return {}
    return handleFormValues(toRaw(unref(formModel)))
  }
  const validateFields = async (nameList?: NamePath[]) => {
    unref(basicFormRef)?.validateFields(nameList)
  }

  const validate = async (nameList?: NamePath[]) => {
    unref(basicFormRef)?.validate(nameList)
  }
  const updateConfig = async (data: Partial<FormConfig> | Partial<FormConfig>[]) => {
    let updateData: Partial<FormConfig>[] = []
    if (isObject(data)) {
      updateData.push(data as FormConfig)
    }
    if (isArray(data)) {
      updateData = [...data]
    }

    const hasField = updateData.every(
      (item) => item.component === 'Divider' || (Reflect.has(item, 'field') && item.field),
    )

    if (!hasField) {
      console.error(
        'All children of the form Schema array that need to be updated must contain the `field` field',
      )
      return
    }
    const schemas: FormConfig[] = []
    updateData.forEach((item) => {
      unref(formConfigRef).forEach((val) => {
        if (val.field === item.field) {
          const newSchema = deepMerge(val, item)
          schemas.push(newSchema)
        } else {
          schemas.push(val)
        }
      })
    })

    unref(getFormProps).config = uniqBy(schemas, 'field')
  }

  const resetFields = async () => {
    const { resetFunc, submitOnReset } = unref(getFormProps)
    resetFunc && isFunction(resetFunc) && (await resetFunc())

    Object.keys(formModel).forEach((key) => {
      formModel[key] = defaultFormValues[key]
    })

    emit('reset', formModel)
    submitOnReset && handleSubmit()
    setTimeout(clearValidate)
    unref(basicFormRef)?.validate()
  }
  const handleSubmit = async (e?: Event) => {
    e && e.preventDefault()
    const { submitFunc } = unref(getFormProps)
    if (submitFunc && isFunction(submitFunc)) {
      await submitFunc()
      return
    }
    const formEl = unref(basicFormRef)
    if (!formEl) {
      return
    }
    try {
      const values = await formEl?.validate()
      const res = handleFormValues(values)
      emit('submit', res)
      return res
    } catch (error: any) {
      console.warn(error)
      Promise.reject(error)
    }
  }
  const clearValidate = async (name: string | string[]) => {
    await unref(basicFormRef)?.clearValidate(name)
  }
  return {
    getFieldsValue,
    validateFields,
    validate,
    resetFields,
    handleSubmit,
    clearValidate,
    setFieldsValue,
    updateConfig,
  }
}
